/*
 * list.c
 *
 *  Created on: Feb 17, 2011
 *      Author: s126135
 */

#include <stdlib.h>
#include "list.h"

list* list_new() {
	list* lista = (list*) malloc(sizeof(list));
	lista->size = 0;
	lista->head = NULL;
	lista->tail = NULL;
	pthread_mutex_init(&lista->mutex, NULL);
	lista->external_lock = 0;
	return lista;
}

void list_free(list* lista) {
	int i;

	for (i = 0; i < lista->size; i++) {
		list_remove(lista, 0);
	}

	pthread_mutex_destroy(&lista->mutex);

	free(lista);
}

int list_size(list* lista) {
	int size;

	
		pthread_mutex_lock(&lista->mutex);

	size = lista->size;

	
			pthread_mutex_unlock(&lista->mutex);

	return size;
}

void list_add(list* lista, void* element, int key) {
	list_element* el;

	
			pthread_mutex_lock(&lista->mutex);

	el = (list_element*) malloc(sizeof(list_element));
	el->key = key;
	el->element = element;
	el->next = NULL;
	if (lista->size == 0) {
		lista->head = el;
		lista->tail = el;
		lista->size++;
	}
	else {
		lista->tail->next = el;
		lista->tail = el;
		lista->size++;
	}

	
			pthread_mutex_unlock(&lista->mutex);
}

void* list_get(list* lista, int index) {
	int i;
	list_element* el;

	if (index >= lista->size)
		return NULL;

	
			pthread_mutex_lock(&lista->mutex);

	el = lista->head;
	for (i = 0; i < index && i < lista->size - 1; i++) {
		el = el->next;
	}

	
			pthread_mutex_unlock(&lista->mutex);

	if (el == NULL)
		return NULL;
	else
		return el->element;
}

void* list_get_key(list* lista, int key) {
	list_element* el;

	
			pthread_mutex_lock(&lista->mutex);

	el = lista->head;
	while (el != NULL) {
		if (el->key == key)
			break;
		el = el->next;
	}

	
			pthread_mutex_unlock(&lista->mutex);

	if (el == NULL)
		return NULL;
	else
		return el->element;
}

void* list_remove(list* lista, int index) {
	int i;
	list_element* el;
	list_element* to_remove;
	void* ret = NULL;

	if (index >= lista->size || index < 0)
		return NULL;

	
			pthread_mutex_lock(&lista->mutex);

	if (index == 0) {
		el = lista->head;
		to_remove = el;
		ret = el->element;
		lista->head = el->next;
		lista->size--;
		if (lista->size == 0)
			lista->tail = NULL;
	}
	else {
		el = lista->head;
		for (i = 0; i < index - 1 && i < lista->size - 2; i++) {
			el = el->next;
		}
		to_remove = el->next;
		ret = to_remove->element;
		el->next = to_remove->next;
		lista->size--;
		if (el->next == NULL)
			lista->tail = el;
	}

	free(to_remove);

	
			pthread_mutex_unlock(&lista->mutex);

	return ret;
}

void* list_remove_key(list* lista, int key) {
	list_element* el;
	list_element* to_remove = NULL;
	void* ret = NULL;

	if (lista->size == 0)
		return NULL;

	
			pthread_mutex_lock(&lista->mutex);

	if (lista->head->key == key) {
		el = lista->head;
		ret = el->element;
		lista->head = el->next;
		lista->size--;
		to_remove = el;
		if (lista->size == 0)
			lista->tail = NULL;
	}
	else {
		el = lista->head;
		while (el->next != NULL && el->next->key != key) {
			el = el->next;
		}
		if (el->next != NULL) {
			to_remove = el->next;
			ret = to_remove->element;
			el->next = to_remove->next;
			lista->size--;
			if (lista->tail == to_remove)
				lista->tail = el;
		}
		else
			ret = NULL;
	}

	if (to_remove)
		free(to_remove);

	
			pthread_mutex_unlock(&lista->mutex);

	return ret;
}

void* list_remove_element(list* lista, void* element) {
	list_element* el;
	list_element* to_remove = NULL;
	void* ret = NULL;

	if (lista == NULL || lista->size == 0)
		return NULL;

	
			pthread_mutex_lock(&lista->mutex);

	if (lista->head->element == element) {
		el = lista->head;
		ret = el->element;
		lista->head = el->next;
		lista->size--;
		to_remove = el;
		if (lista->size == 0)
			lista->tail = NULL;
	}
	else {
		el = lista->head;
		while (el->next != NULL && el->next->element != element) {
			el = el->next;
		}
		if (el->next != NULL) {
			to_remove = el->next;
			ret = to_remove->element;
			el->next = to_remove->next;
			lista->size--;
			if (to_remove->next == NULL)
				lista->tail = el;
		}
	}

	if (to_remove)
		free(to_remove);

	
			pthread_mutex_unlock(&lista->mutex);

	return ret;
}

void list_lock(list* lista) {
	// TODO: non va bene così
	pthread_mutex_lock(&lista->mutex);
	lista->external_lock = 1;
}

void list_unlock(list* lista) {
	// TODO: non va bene così
	lista->external_lock = 0;
	pthread_mutex_unlock(&lista->mutex);
}
